﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: ViewDefCategoryViewModel 
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using VmroleAuthor.Commands;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class ViewDefCategoryViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults, ICanBeRemovedFromTree
    {
        public const string VMCategoryDefaultValue = "VM Category";
        public const string ApplicationCategoryDefaultValue = "Application Category";

        public const string VMCategoryAutoCreatedByToolMoniker = "VM Category AutoCreatedByToolMoniker";
        public const string ApplicationCategoryAutoCreatedByToolMoniker = "Application Category AutoCreatedByToolMoniker";

        private readonly ViewDefCategory viewDefCategory;
        private readonly ViewDefViewModel viewDefViewModel;
        private readonly VmRoleResourceDefinitionViewModel resourceDefinitionViewModel;
        private readonly ObservableCollection<ViewDefParameterViewModel> viewDefParameterViewModels;

        private ViewDefParameterLocalizablePropertyViewModel categoryName;    

        public static class PropertyNames
        {
            public const string Summary = "Summary";
            public const string CategoryName = "CategoryName";
            public const string ViewDefParameterViewModels = "ViewDefParameterViewModels";
            public const string ViewDefCategory = "ViewDefCategory";
        }

        public ViewDefCategoryViewModel(ViewDefCategory viewDefCategory, ViewDefSectionViewModel viewDefSectionViewModel, ViewDefViewModel viewDefViewModel, VmRoleResourceDefinitionViewModel resourceDefinitionViewModel)
            : base(viewDefSectionViewModel, false)
        {
            if (viewDefCategory == null)
            {
                viewDefCategory = new ViewDefCategory();
            }
            this.viewDefCategory = viewDefCategory;
            this.viewDefViewModel = viewDefViewModel;
            this.resourceDefinitionViewModel = resourceDefinitionViewModel;           
            if (this.viewDefCategory == null)
            {
                this.viewDefCategory = new ViewDefCategory();
            }      
            this.viewDefParameterViewModels = new ObservableCollection<ViewDefParameterViewModel>();    
            if (this.viewDefCategory.Parameters != null)
            {
                foreach (ViewDefParameter viewDefParameter in this.viewDefCategory.Parameters)
                {
                    var viewDefParameterViewModel = new ViewDefParameterViewModel(viewDefParameter,this, this.viewDefViewModel, false);
                    this.viewDefParameterViewModels.Add(viewDefParameterViewModel);
                }
            }

            this.AddViewDefParameterCommand = new RelayCommand(
               param => this.AddViewDefParameter((ParameterViewModel)param),
               param => this.CanAddViewDefParameter((ParameterViewModel)param));

            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();

            if (!string.IsNullOrEmpty(this.viewDefCategory.CategoryName))
            {
                if (string.Equals(this.viewDefCategory.CategoryName, VMCategoryAutoCreatedByToolMoniker, StringComparison.Ordinal))
                {
                    this.IsThisVMCategoryAutoCreatedByTool = true;
                }
                else if (string.Equals(this.viewDefCategory.CategoryName, ApplicationCategoryAutoCreatedByToolMoniker, StringComparison.Ordinal))
                {
                    this.IsThisApplicationCategoryAutoCreatedByTool = true;
                }
            }

            if (this.resourceDefinitionViewModel.IsNewResdefCreatedByTheTool)
            {
                this.CategoryName.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
            }
        }

        public bool IsThisVMCategoryAutoCreatedByTool { get; private set; }

        public bool IsThisApplicationCategoryAutoCreatedByTool { get; private set; }

        public string Summary
        {
            get
            {                      
                return "Category " + (this.CategoryName.PropertyValue ?? string.Empty);
            }
        }

        public ViewDefCategory ViewDefCategory
        {
            get { return this.viewDefCategory; }
        }

        public ViewDefParameterLocalizablePropertyViewModel CategoryName
        {
            get
            {
                if (this.categoryName == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefCategory.CategoryName;
                    Action<string> paramValueSetter = delegate(string value)
                        {
                            this.viewDefCategory.CategoryName = value;
                            this.OnPropertyChanged(PropertyNames.Summary);
                        };
                    Func<string> autoParamNameGetter = () =>
                        {
                            if (this.IsThisVMCategoryAutoCreatedByTool)
                            {
                                return "VM" + PropertyNames.CategoryName;
                            }
                            if (this.IsThisApplicationCategoryAutoCreatedByTool)
                            {
                                return "Application" + PropertyNames.CategoryName;
                            }
                            return PropertyNames.CategoryName;                            
                        };
                   
                    Func<string> someInitialValue = () =>
                    {
                        if (this.IsThisVMCategoryAutoCreatedByTool || (this.viewDefCategory.CategoryName != null && this.viewDefCategory.CategoryName.IndexOf("VM", StringComparison.OrdinalIgnoreCase) >= 0))
                        {
                            return VMCategoryDefaultValue;
                        }
                        if (this.IsThisApplicationCategoryAutoCreatedByTool || (this.viewDefCategory.CategoryName != null && this.viewDefCategory.CategoryName.IndexOf("Application", StringComparison.OrdinalIgnoreCase) >= 0))
                        {
                            return ApplicationCategoryDefaultValue;
                        }
                        return "VMRoleCategorySettings";
                    };

                    this.categoryName = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.CategoryName,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        true,
                        someInitialValue);
                }
                return this.categoryName;
            }
        }

        public ObservableCollection<ViewDefParameterViewModel> ViewDefParameterViewModels
        {
            get { return this.viewDefParameterViewModels; }
        }

        public ObservableCollection<ParameterViewModel> AvailableParameters
        {
            get
            {
                ObservableCollection<ParameterViewModel> available = new ObservableCollection<ParameterViewModel>(resourceDefinitionViewModel.ResourceDefinitionParametersViewModel.Parameters);
                foreach (ViewDefParameterViewModel viewDefParameterViewModel in viewDefParameterViewModels)
                {
                    ViewDefParameterViewModel model = viewDefParameterViewModel;
                    available.Remove(param => param.Name.Equals(model.Name, StringComparison.Ordinal));
                }
                return available;
            }
        }       

        public ICommand AddViewDefParameterCommand
        {
            get;
            private set;
        }

        public bool CanAddViewDefParameter(ParameterViewModel resdefParamViewModel)
        {           
            return resdefParamViewModel != null && (this.viewDefParameterViewModels.FirstOrDefault(viewDefParam => viewDefParam.Name.Equals(resdefParamViewModel.Name, StringComparison.Ordinal)) == null);         
        }

        public void AddViewDefParameter(ParameterViewModel resdefParamViewModel)
        {
            ViewDefParameter preExistingViewDefParameter = this.viewDefViewModel.GetViewDefParameter(resdefParamViewModel.Name);           
            this.AddViewDefParameter(resdefParamViewModel, false, preExistingViewDefParameter);
        }

        public void AddViewDefParameter(ParameterViewModel resdefParamViewModel, bool autoLocalizeSomeProperties, ViewDefParameter preExistingViewDefParameter = null)
        {
            if (!CanAddViewDefParameter(resdefParamViewModel))
            {
                return;
            }
            if (this.viewDefCategory.Parameters == null)
            {
                this.viewDefCategory.Parameters = new List<ViewDefParameter>();
            }
            ViewDefParameter viewDefParameter = new ViewDefParameter();
            viewDefParameter.Name = resdefParamViewModel.Name;
            viewDefParameter.Label = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.DefaultEnglishLabelText : preExistingViewDefParameter.Label;
            viewDefParameter.Type = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.ViewDefParameterType.ToString() : preExistingViewDefParameter.Type;
            if (resdefParamViewModel.ViewDefParamInfo.ViewDefParameterType == ViewDefParameterType.Option)
            {              
                if (resdefParamViewModel.ViewDefParamInfo.Options == null)
                {
                    viewDefParameter.Type = ViewDefParameterType.String.ToString();
                }
                else
                {
                    viewDefParameter.OptionValues = new List<OptionValue>();
                    foreach (string option in resdefParamViewModel.ViewDefParamInfo.Options)
                    {
                        viewDefParameter.OptionValues.Add(new OptionValue { Key = option, Value = option});
                    }
                }
            }
            viewDefParameter.DefaultValue = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.DeEscapedDefaultInitialValue : preExistingViewDefParameter.DefaultValue;
            viewDefParameter.Description =  preExistingViewDefParameter == null ? resdefParamViewModel.Description : preExistingViewDefParameter.Description;
            viewDefParameter.Configurable = preExistingViewDefParameter == null || preExistingViewDefParameter.Configurable;       
            viewDefParameter.Validation = new ViewDefParameterValidation();
            Debug.Assert(preExistingViewDefParameter == null || preExistingViewDefParameter.Validation != null);
            viewDefParameter.Validation.Required =  preExistingViewDefParameter == null ? string.IsNullOrEmpty(resdefParamViewModel.ViewDefParamInfo.DefaultInitialValue) : preExistingViewDefParameter.Validation.Required; 
            viewDefParameter.Validation.MinLength = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.MinLength : preExistingViewDefParameter.Validation.MinLength;
            viewDefParameter.Validation.MaxLength = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.MaxLength : preExistingViewDefParameter.Validation.MaxLength;
            viewDefParameter.Validation.MinRange = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.MinRange : preExistingViewDefParameter.Validation.MinRange;
            viewDefParameter.Validation.MaxRange = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.MaxRange : preExistingViewDefParameter.Validation.MaxRange;
            viewDefParameter.Validation.RegExp = preExistingViewDefParameter == null ? resdefParamViewModel.ViewDefParamInfo.RegExp : preExistingViewDefParameter.Validation.RegExp;
            
            
            viewDefParameter.Validation.Messages = new ViewDefParameterValidationMessages();
            Debug.Assert(preExistingViewDefParameter == null || (preExistingViewDefParameter.Validation != null && preExistingViewDefParameter.Validation.Messages != null));
            viewDefParameter.Validation.Messages.Required = preExistingViewDefParameter == null ? string.Format("{0} is required", viewDefParameter.Label) : preExistingViewDefParameter.Validation.Messages.Required;
            if (preExistingViewDefParameter != null)
            {
                viewDefParameter.Validation.Messages.Length = preExistingViewDefParameter.Validation.Messages.Length;
                viewDefParameter.Validation.Messages.Range = preExistingViewDefParameter.Validation.Messages.Range;
                viewDefParameter.Validation.Messages.RegExp = preExistingViewDefParameter.Validation.Messages.RegExp;
            }
            var viewDefParameterViewModel = new ViewDefParameterViewModel(viewDefParameter, this, this.viewDefViewModel, autoLocalizeSomeProperties);
            this.viewDefParameterViewModels.Add(viewDefParameterViewModel);
            if (this.viewDefCategory.Parameters == null)
            {
                this.viewDefCategory.Parameters = new List<ViewDefParameter>();
            }
            this.viewDefCategory.Parameters.Add(viewDefParameter);
            // Now Remove this from other category if it exists     
            if (preExistingViewDefParameter != null)
            {
                this.viewDefViewModel.RemoveViewDefParameter(viewDefParameter.Name, viewDefParameterViewModel);
            }           
        }
      
        public bool CanMoveViewDefParameterUp(ViewDefParameterViewModel viewDefParameterViewModel)
        {
            return this.viewDefParameterViewModels.IndexOf(viewDefParameterViewModel) >= 1;
        }

        public void MoveViewDefParameterUp(ViewDefParameterViewModel viewDefParameterViewModel)
        {
            int index = this.viewDefParameterViewModels.IndexOf(viewDefParameterViewModel);
            int oldViewDefIndex = this.viewDefCategory.Parameters.IndexOf(viewDefParameterViewModel.ViewDefParameter);
            if (index >= 1 && oldViewDefIndex >= 1)
            {
                ViewDefParameter viewDefParameter = this.viewDefCategory.Parameters[oldViewDefIndex];
                int newViewDefIndex = oldViewDefIndex -1;
                this.viewDefCategory.Parameters.RemoveAt(oldViewDefIndex);
                this.viewDefCategory.Parameters.Insert(newViewDefIndex, viewDefParameter);
                int newIndex = index - 1;
                this.viewDefParameterViewModels.RemoveAt(index);
                this.viewDefParameterViewModels.Insert(newIndex, viewDefParameterViewModel);
            }
        }

        public bool CanMoveViewDefParameterDown(ViewDefParameterViewModel viewDefParameterViewModel)
        {
            int index = this.viewDefParameterViewModels.IndexOf(viewDefParameterViewModel);
            return index > -1 && index < this.viewDefParameterViewModels.Count - 1;
        }

        public void MoveViewDefParameterDown(ViewDefParameterViewModel viewDefParameterViewModel)
        {
            int index = this.viewDefParameterViewModels.IndexOf(viewDefParameterViewModel);
            int oldViewDefIndex = this.viewDefCategory.Parameters.IndexOf(viewDefParameterViewModel.ViewDefParameter);
            if ((index > -1 && index < this.viewDefParameterViewModels.Count - 1) &&
                (oldViewDefIndex > -1 && oldViewDefIndex < this.viewDefCategory.Parameters.Count - 1))
            {                               
                ViewDefParameter viewDefParameter = this.viewDefCategory.Parameters[oldViewDefIndex];
                int newViewDefIndex = oldViewDefIndex + 1;
                this.viewDefCategory.Parameters.RemoveAt(oldViewDefIndex);
                this.viewDefCategory.Parameters.Insert(newViewDefIndex, viewDefParameter);
                int newIndex = index + 1;
                this.viewDefParameterViewModels.RemoveAt(index);
                this.viewDefParameterViewModels.Insert(newIndex, viewDefParameterViewModel);
            }
        }
      
        protected override void LoadChildren()
        {            
        }

        public void Cleanup()
        {
            this.IsExpanded = true;
            ParametersAndPayloadCleanupHelper.CleanupParametersAndPayload(this, typeof(PropertyNames));
        }

        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.ViewDefCategory:
                        if (this.viewDefCategory.Parameters == null || this.viewDefCategory.Parameters.Count == 0)
                        {
                            error = "Need to have atleast one parameter under this ViewDef category";
                        }
                        break;
                }
                return error;              
            }
        }

        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error           
        }
    }
}
